<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>KDJ指标详解与计算器</title>
    <style>
        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }
        
        body {
            background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
            color: #333;
            line-height: 1.6;
            padding: 20px;
            max-width: 1200px;
            margin: 0 auto;
        }
        
        header {
            text-align: center;
            margin-bottom: 40px;
            padding: 20px;
            background: white;
            border-radius: 10px;
            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
        }
        
        h1 {
            color: #2c3e50;
            margin-bottom: 10px;
        }
        
        .subtitle {
            color: #7f8c8d;
            font-size: 1.2rem;
        }
        
        .container {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 30px;
        }
        
        @media (max-width: 900px) {
            .container {
                grid-template-columns: 1fr;
            }
        }
        
        .card {
            background: white;
            border-radius: 10px;
            padding: 25px;
            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
        }
        
        .calculator {
            grid-column: 1 / -1;
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 30px;
        }
        
        @media (max-width: 900px) {
            .calculator {
                grid-template-columns: 1fr;
            }
        }
        
        h2 {
            color: #3498db;
            margin-bottom: 20px;
            padding-bottom: 10px;
            border-bottom: 2px solid #3498db;
        }
        
        h3 {
            color: #2c3e50;
            margin: 15px 0 10px;
        }
        
        p {
            margin-bottom: 15px;
        }
        
        ul, ol {
            margin-left: 20px;
            margin-bottom: 20px;
        }
        
        li {
            margin-bottom: 8px;
        }
        
        .formula {
            background: #f8f9fa;
            padding: 15px;
            border-left: 4px solid #3498db;
            margin: 20px 0;
            font-family: 'Courier New', monospace;
            border-radius: 0 5px 5px 0;
        }
        
        .input-group {
            margin-bottom: 15px;
        }
        
        label {
            display: block;
            margin-bottom: 5px;
            font-weight: 600;
            color: #2c3e50;
        }
        
        input, button {
            padding: 12px;
            border: 1px solid #ddd;
            border-radius: 5px;
            width: 100%;
            font-size: 16px;
        }
        
        button {
            background: #3498db;
            color: white;
            border: none;
            cursor: pointer;
            transition: background 0.3s;
            font-weight: 600;
            margin-top: 10px;
        }
        
        button:hover {
            background: #2980b9;
        }
        
        .results {
            margin-top: 20px;
        }
        
        table {
            width: 100%;
            border-collapse: collapse;
            margin-top: 15px;
        }
        
        th, td {
            padding: 12px 15px;
            text-align: center;
            border: 1px solid #ddd;
        }
        
        th {
            background: #f2f2f2;
            font-weight: 600;
        }
        
        tr:nth-child(even) {
            background: #f9f9f9;
        }
        
        .chart-container {
            height: 400px;
            margin-top: 30px;
        }
        
        .interpretation {
            background: #e8f4fc;
            padding: 20px;
            border-radius: 5px;
            margin-top: 20px;
        }
        
        .signal {
            display: flex;
            align-items: center;
            margin: 10px 0;
            padding: 10px;
            border-radius: 5px;
        }
        
        .buy {
            background: #e8f6f3;
            border-left: 4px solid #2ecc71;
        }
        
        .sell {
            background: #fceae9;
            border-left: 4px solid #e74c3c;
        }
        
        .neutral {
            background: #fef9e7;
            border-left: 4px solid #f1c40f;
        }
        
        .icon {
            font-size: 24px;
            margin-right: 15px;
        }
        
        footer {
            text-align: center;
            margin-top: 40px;
            padding: 20px;
            color: #7f8c8d;
            font-size: 0.9rem;
        }
    </style>
</head>
<body>
    <header>
        <h1>KDJ指标详解与计算器</h1>
        <p class="subtitle">理解并计算股票技术分析中的重要指标</p>
    </header>
    
    <div class="container">
        <div class="card">
            <h2>什么是KDJ指标？</h2>
            <p>KDJ指标，又称随机指标，是股票技术分析中常用的超买超卖指标。它由George Lane首创，通过计算特定周期内最高价、最低价及收盘价之间的关系，反映价格走势的强弱和转折点。</p>
            
            <h3>KDJ的组成</h3>
            <ul>
                <li><strong>K线</strong>：快速确认线，反映价格变化的敏感线</li>
                <li><strong>D线</strong>：慢速主干线，K线的移动平均</li>
                <li><strong>J线</strong>：方向敏感线，反映K值与D值的乖离程度</li>
            </ul>
            
            <h3>计算公式</h3>
            <div class="formula">
                <p>RSV = (收盘价 - N日内最低价) / (N日内最高价 - N日内最低价) × 100</p>
                <p>K值 = 2/3 × 前一日K值 + 1/3 × 当日RSV</p>
                <p>D值 = 2/3 × 前一日D值 + 1/3 × 当日K值</p>
                <p>J值 = 3 × 当日K值 - 2 × 当日D值</p>
            </div>
            
            <h3>参数说明</h3>
            <p>默认参数通常为(9,3,3)，表示：</p>
            <ul>
                <li>计算RSV的周期为9天</li>
                <li>K值对RSV作3日指数平滑移动平均</li>
                <li>D值对K值作3日指数平滑移动平均</li>
            </ul>
        </div>
        
        <div class="card">
            <h2>KDJ指标的应用</h2>
            
            <h3>超买超卖判断</h3>
            <ul>
                <li>K、D值在80以上为超买区，市场可能回调</li>
                <li>K、D值在20以下为超卖区，市场可能反弹</li>
                <li>J值超过100为超买，低于0为超卖</li>
            </ul>
            
            <h3>金叉与死叉</h3>
            <ul>
                <li><strong>金叉</strong>：K线从下向上突破D线，买入信号</li>
                <li><strong>死叉</strong>：K线从上向下跌破D线，卖出信号</li>
            </ul>
            
            <h3>背离现象</h3>
            <ul>
                <li><strong>顶背离</strong>：股价创新高，KDJ指标未创新高，可能见顶</li>
                <li><strong>底背离</strong>：股价创新低，KDJ指标未创新低，可能见底</li>
            </ul>
            
            <div class="interpretation">
                <h3>信号解读</h3>
                
                <div class="signal buy">
                    <div class="icon">📈</div>
                    <div>
                        <strong>买入信号</strong>
                        <p>K、D值低于20，J值低于0，且K线从下向上突破D线形成金叉</p>
                    </div>
                </div>
                
                <div class="signal sell">
                    <div class="icon">📉</div>
                    <div>
                        <strong>卖出信号</strong>
                        <p>K、D值高于80，J值高于100，且K线从上向下跌破D线形成死叉</p>
                    </div>
                </div>
                
                <div class="signal neutral">
                    <div class="icon">↔️</div>
                    <div>
                        <strong>观望信号</strong>
                        <p>K、D值在20-80之间波动，市场处于震荡状态，方向不明</p>
                    </div>
                </div>
            </div>
        </div>
        
        <div class="calculator card">
            <div>
                <h2>KDJ计算器</h2>
                
                <div class="input-group">
                    <label for="priceData">股票数据（日期,最高价,最低价,收盘价）</label>
                    <textarea id="priceData" rows="6" placeholder="每行输入一条记录，格式为：日期,最高价,最低价,收盘价
例如：
2023-01-03,105.5,103.2,104.8
2023-01-04,107.8,104.5,106.3
2023-01-05,108.9,106.1,107.5">日期,最高价,最低价,收盘价
2023-01-03,105.5,103.2,104.8
2023-01-04,107.8,104.5,106.3
2023-01-05,108.9,106.1,107.5
2023-01-06,109.7,107.2,108.9
2023-01-09,111.2,108.6,110.5
2023-01-10,112.8,110.1,111.9
2023-01-11,113.5,110.8,112.3
2023-01-12,114.2,112.5,113.8
2023-01-13,115.9,113.6,114.7
2023-01-16,116.5,114.2,115.8
2023-01-17,117.2,115.3,116.5
2023-01-18,118.0,116.2,117.3
2023-01-19,117.8,115.9,116.7
2023-01-20,118.5,116.8,117.9</textarea>
                </div>
                
                <div class="input-group">
                    <label for="nPeriod">N周期（默认9）</label>
                    <input type="number" id="nPeriod" value="9" min="1">
                </div>
                
                <div class="input-group">
                    <label for="kPeriod">K值平滑周期（默认3）</label>
                    <input type="number" id="kPeriod" value="3" min="1">
                </div>
                
                <div class="input-group">
                    <label for="dPeriod">D值平滑周期（默认3）</label>
                    <input type="number" id="dPeriod" value="3" min="1">
                </div>
                
                <button id="calculateBtn">计算KDJ指标</button>
            </div>
            
            <div>
                <h2>计算结果</h2>
                <div class="results">
                    <table id="resultTable">
                        <thead>
                            <tr>
                                <th>日期</th>
                                <th>收盘价</th>
                                <th>K值</th>
                                <th>D值</th>
                                <th>J值</th>
                            </tr>
                        </thead>
                        <tbody>
                            <!-- 结果将通过JavaScript填充 -->
                        </tbody>
                    </table>
                </div>
                
                <div class="interpretation" id="interpretation">
                    <h3>当前信号解读</h3>
                    <p>请输入数据并计算后显示解读</p>
                </div>
            </div>
        </div>
    </div>
    
    <footer>
        <p>© 2023 股票分析工具 | KDJ指标详解与计算器 | 本工具仅供参考，不构成投资建议</p>
    </footer>

    <script>
        document.getElementById('calculateBtn').addEventListener('click', function() {
            // 获取输入数据
            const priceData = document.getElementById('priceData').value.trim();
            const nPeriod = parseInt(document.getElementById('nPeriod').value);
            const kPeriod = parseInt(document.getElementById('kPeriod').value);
            const dPeriod = parseInt(document.getElementById('dPeriod').value);
            
            // 解析数据
            const lines = priceData.split('\n').filter(line => line.trim() !== '');
            const data = [];
            
            // 跳过标题行（如果有）
            const startIndex = lines[0].includes('日期') ? 1 : 0;
            
            for (let i = startIndex; i < lines.length; i++) {
                const parts = lines[i].split(',');
                if (parts.length >= 4) {
                    data.push({
                        date: parts[0].trim(),
                        high: parseFloat(parts[1]),
                        low: parseFloat(parts[2]),
                        close: parseFloat(parts[3])
                    });
                }
            }
            
            // 计算KDJ
            const kdjResults = calculateKDJ(data, nPeriod, kPeriod, dPeriod);
            
            // 显示结果
            displayResults(kdjResults);
            
            // 显示解读
            interpretKDJ(kdjResults);
        });
        
        function calculateKDJ(data, n = 9, kSmooth = 3, dSmooth = 3) {
            const results = [];
            let prevK = 50; // 初始K值
            let prevD = 50; // 初始D值
            
            for (let i = 0; i < data.length; i++) {
                if (i < n - 1) {
                    // 前n-1天数据不足，无法计算
                    results.push({
                        date: data[i].date,
                        close: data[i].close,
                        k: null,
                        d: null,
                        j: null
                    });
                    continue;
                }
                
                // 计算最近n天的最高价和最低价
                let highestHigh = -Infinity;
                let lowestLow = Infinity;
                
                for (let j = i - n + 1; j <= i; j++) {
                    if (data[j].high > highestHigh) highestHigh = data[j].high;
                    if (data[j].low < lowestLow) lowestLow = data[j].low;
                }
                
                // 计算RSV
                const rsv = ((data[i].close - lowestLow) / (highestHigh - lowestLow)) * 100;
                
                // 计算K值
                const k = (prevK * (kSmooth - 1) + rsv) / kSmooth;
                
                // 计算D值
                const d = (prevD * (dSmooth - 1) + k) / dSmooth;
                
                // 计算J值
                const j = 3 * k - 2 * d;
                
                // 更新前一天的K值和D值
                prevK = k;
                prevD = d;
                
                results.push({
                    date: data[i].date,
                    close: data[i].close,
                    k: parseFloat(k.toFixed(2)),
                    d: parseFloat(d.toFixed(2)),
                    j: parseFloat(j.toFixed(2))
                });
            }
            
            return results;
        }
        
        function displayResults(results) {
            const tbody = document.querySelector('#resultTable tbody');
            tbody.innerHTML = '';
            
            for (const result of results) {
                const row = document.createElement('tr');
                
                row.innerHTML = `
                    <td>${result.date}</td>
                    <td>${result.close}</td>
                    <td>${result.k !== null ? result.k : '-'}</td>
                    <td>${result.d !== null ? result.d : '-'}</td>
                    <td>${result.j !== null ? result.j : '-'}</td>
                `;
                
                tbody.appendChild(row);
            }
        }
        
        function interpretKDJ(results) {
            if (results.length < 1) return;
            
            const lastResult = results[results.length - 1];
            
            if (lastResult.k === null || lastResult.d === null) {
                document.getElementById('interpretation').innerHTML = `
                    <h3>当前信号解读</h3>
                    <p>数据不足，无法计算有效的KDJ值</p>
                `;
                return;
            }
            
            let signal = '';
            let explanation = '';
            
            if (lastResult.k < 20 && lastResult.d < 20 && lastResult.j < 0) {
                signal = '强烈买入信号';
                explanation = 'K、D值均低于20，J值低于0，市场处于超卖状态，可能即将反弹';
            } else if (lastResult.k > 80 && lastResult.d > 80 && lastResult.j > 100) {
                signal = '强烈卖出信号';
                explanation = 'K、D值均高于80，J值高于100，市场处于超买状态，可能即将回调';
            } else if (lastResult.k < 30 && lastResult.d < 30) {
                signal = '买入信号';
                explanation = 'K、D值均低于30，市场处于弱势，可能即将反弹';
            } else if (lastResult.k > 70 && lastResult.d > 70) {
                signal = '卖出信号';
                explanation = 'K、D值均高于70，市场处于强势，可能即将回调';
            } else {
                signal = '观望信号';
                explanation = 'K、D值在30-70之间，市场处于震荡状态，方向不明';
            }
            
            // 检查金叉死叉
            if (results.length >= 2) {
                const prevResult = results[results.length - 2];
                
                if (prevResult.k !== null && prevResult.d !== null) {
                    if (prevResult.k < prevResult.d && lastResult.k > lastResult.d) {
                        explanation += '，且出现金叉，上涨可能性增大';
                    } else if (prevResult.k > prevResult.d && lastResult.k < lastResult.d) {
                        explanation += '，且出现死叉，下跌可能性增大';
                    }
                }
            }
            
            document.getElementById('interpretation').innerHTML = `
                <h3>当前信号解读</h3>
                <p><strong>${signal}</strong>: ${explanation}</p>
                <p>当前值: K=${lastResult.k}, D=${lastResult.d}, J=${lastResult.j}</p>
            `;
        }
    </script>
</body>
</html>